#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
打包部署脚本
该脚本用于清理测试数据、设置生产环境并打包应用程序以便部署。
"""

import os
import sys
import shutil
import subprocess
import datetime
import zipfile

# 设置路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
OUTPUT_DIR = os.path.join(os.path.dirname(BASE_DIR), 'deployment_packages')

# 确保输出目录存在
os.makedirs(OUTPUT_DIR, exist_ok=True)

def run_command(command, cwd=None):
    """运行命令并返回结果"""
    print(f"运行命令: {' '.join(command)}")
    try:
        result = subprocess.run(
            command,
            cwd=cwd or BASE_DIR,
            check=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        print(result.stdout)
        return True
    except subprocess.CalledProcessError as e:
        print(f"命令执行失败: {e}")
        print(e.stderr)
        return False

def clear_test_data():
    """清除测试数据"""
    print("\n===== 清除测试数据 =====")
    return run_command([sys.executable, 'clear_test_data.py'])

def create_production_config():
    """创建生产环境配置"""
    print("\n===== 创建生产环境配置 =====")
    config_content = """
# 生产环境配置
import os

base_dir = os.path.abspath(os.path.dirname(__file__))

class Config:
    # 安全配置
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'a_secure_random_key_for_production'
    
    # 数据库配置
    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(base_dir, 'issues.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    
    # 文件上传配置
    UPLOAD_FOLDER = os.path.join(base_dir, 'uploads')
    ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'mp3', 'mp4', 'avi', 'mov'}
    
    # 生产环境特有配置
    DEBUG = False
    TESTING = False
    """
    config_path = os.path.join(BASE_DIR, 'config.py')
    with open(config_path, 'w', encoding='utf-8') as f:
        f.write(config_content)
    print(f"已创建生产环境配置文件: {config_path}")
    return True

def update_app_for_production():
    """更新应用以使用生产环境配置"""
    print("\n===== 更新应用以使用生产环境配置 =====")
    app_py_path = os.path.join(BASE_DIR, 'app.py')
    
    # 读取当前app.py内容
    with open(app_py_path, 'r', encoding='utf-8') as f:
        app_content = f.read()
    
    # 修改app.py以导入生产配置
    config_import = 'from config import Config\napp.config.from_object(Config)' 
    if 'import os' in app_content and config_import not in app_content:
        new_content = app_content.replace(
            'import os',
            'import os\nfrom config import Config' 
        ).replace(
            'app = Flask(__name__)',
            'app = Flask(__name__)\napp.config.from_object(Config)'
        )
        
        # 移除或注释掉硬编码的配置
        new_content = new_content.replace(
            'app.config[''SECRET_KEY''] = ''your-secret-key-here''',
            '# app.config[''SECRET_KEY''] = ''your-secret-key-here''  # 使用配置文件中的设置'
        ).replace(
            'app.config[''SQLALCHEMY_DATABASE_URI''] = ''sqlite:///'' + os.path.join(base_dir, ''issues.db'')',
            '# app.config[''SQLALCHEMY_DATABASE_URI''] = ''sqlite:///'' + os.path.join(base_dir, ''issues.db'')  # 使用配置文件中的设置'
        ).replace(
            'app.config[''SQLALCHEMY_TRACK_MODIFICATIONS''] = False',
            '# app.config[''SQLALCHEMY_TRACK_MODIFICATIONS''] = False  # 使用配置文件中的设置'
        )
        
        with open(app_py_path, 'w', encoding='utf-8') as f:
            f.write(new_content)
        print(f"已更新 {app_py_path} 以使用生产环境配置")
        return True
    else:
        print("app.py 已经配置为使用生产环境配置或结构不匹配")
        return True

def create_deployment_zip():
    """创建部署ZIP包"""
    print("\n===== 创建部署ZIP包 =====")
    
    # 生成唯一的包名
    timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    package_name = f'issue_tracker_deployment_{timestamp}.zip'
    package_path = os.path.join(OUTPUT_DIR, package_name)
    
    # 需要包含的文件和目录
    include_items = [
        'app.py',
        'config.py',
        'database.py',
        'init_db.py',
        'models',
        'views',
        'static',
        'templates',
        'requirements.txt',
        'uploads'  # 包含空的上传目录
    ]
    
    # 需要排除的文件和目录
    exclude_patterns = [
        '__pycache__',
        '*.pyc',
        '*.pyo',
        '*.pyd',
        'issues.db',  # 不包含数据库文件，将在目标环境初始化
        '*.log',
        '*.sqlite-journal',
        'deployment_packages',
        'generate_fake_data.py',
        'clear_test_data.py',
        'package_for_deployment.py',
        'set_admin.py',
        'check_users.py',
        'recreate_db.py'
    ]
    
    # 创建ZIP文件
    with zipfile.ZipFile(package_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for item in include_items:
            item_path = os.path.join(BASE_DIR, item)
            
            if os.path.isfile(item_path):
                zipf.write(item_path, arcname=item)
                print(f"添加文件: {item}")
            elif os.path.isdir(item_path):
                # 添加目录中的所有文件，但排除不需要的文件
                for root, dirs, files in os.walk(item_path):
                    # 从dirs中移除要排除的目录
                    dirs[:] = [d for d in dirs if d not in exclude_patterns]
                    
                    for file in files:
                        if not any(exclude in file for exclude in exclude_patterns):
                            file_path = os.path.join(root, file)
                            arcname = os.path.relpath(file_path, BASE_DIR)
                            zipf.write(file_path, arcname=arcname)
                            print(f"添加文件: {arcname}")
    
    print(f"部署包已创建: {package_path}")
    return package_path

def main():
    """主函数"""
    print("===== 开始打包部署应用 =====")
    
    # 1. 清除测试数据
    if not clear_test_data():
        print("清除测试数据失败，中止打包")
        return
    
    # 2. 创建生产环境配置
    if not create_production_config():
        print("创建生产环境配置失败，中止打包")
        return
    
    # 3. 更新应用以使用生产环境配置
    if not update_app_for_production():
        print("更新应用配置失败，中止打包")
        return
    
    # 4. 创建部署ZIP包
    package_path = create_deployment_zip()
    
    # 5. 还原本地开发配置
    # 注意：在实际生产环境中，您可能需要更复杂的配置管理策略
    print("\n===== 打包完成 =====")
    print(f"部署包位置: {package_path}")
    print("请参考DEPLOYMENT.md文件了解部署说明")

if __name__ == "__main__":
    main()